using System;
using System.Configuration;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Text;

namespace YUI.Controls
{
	public enum ColorPickerMode
	{
		/// <summary>
		/// The color picker widget will be rendered into a movable virtual window within the document
		/// </summary>
		FloatingDialog,

		/// <summary>
		/// The color picker will be statically rendered into the control specified by the ContainerControl property
		/// </summary>
		Inline
	}

	public enum ColorPickerUpdateMode
	{
		/// <summary>
		/// Whenever the user clicks on a color, the callback and client code will be called.
		/// </summary>
		Continuous,

		/// <summary>
		/// Used on dialogs only (FloatingDialog)
		/// </summary>
		OnConfirm
	}

	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	//[DefaultProperty("ContainerControl")]
	[ToolboxData("<{0}:ColorPicker run=\"server\"> </{0}:ColorPicker>")]
	//[ParseChildren(true, "Text")]
	public class ColorPicker : WebControl, ICallbackEventHandler
	{
		private string ContainerClientID
		{
			get
			{
				return ClientID + "_container";
			}
		}
		private string HiddenFieldClientID
		{
			get
			{
				return ClientID + "_value";
			}
		}
		private string DialogContainerClientID
		{
			get
			{
				return ClientID + "_dialog";
			}
		}

		#region Behavior properties
		[Bindable(false)]
		[Category("Behavior")]
		[Description("Color picker mode. One of: FloatingDialog, Inline.")]
		[DefaultValue("")]
		[Localizable(false)]
		public ColorPickerMode ColorPickerMode { get; set; }

		[Bindable(true)]
		[Category("Behavior")]
		[Description("The update mode of the Color Picker. Either OnConfirm or Continuous.")]
		[DefaultValue("")]
		[Localizable(false)]
		public ColorPickerUpdateMode UpdateMode { get; set; }

		[Bindable(false)]
		[Category("Behavior")]
		[Description("Enables animation of the color picker thumb")]
		[DefaultValue(false)]
		[Localizable(false)]
		public bool EnableAnimation { get; set; }

		[Bindable(false)]
		[Category("Behavior")]
		[Description("Color picker modal behavior.")]
		[DefaultValue("")]
		[Localizable(false)]
		public bool ModalBehavior { get; set; }

		[Bindable(false)]
		[Category("Behavior")]
		[Description("Color picker modal behavior.")]
		[DefaultValue("")]
		[Localizable(false)]
		public bool UseCallback { get; set; }
		#endregion

		#region Color Picker Properties

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("The Color picker RGB Value")]
		[DefaultValue("")]
		[Localizable(false)]
		public string Color
		{
			get
			{
				return (string)ViewState["Color"];

			}
			set
			{
				if (Color != value)
				{
					ViewState["Color"] = value;
					if (ColorChanged != null)
						ColorChanged(this, EventArgs.Empty);
				}
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Controls whether the color picker should be rendered as visible or hidden")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool IsHidden
		{
			get
			{
				var vsp = ViewState["IsHidden"];
				if (vsp == null)
					return false;
				return (bool)vsp;
			}
			set
			{
				ViewState["IsHidden"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the entire set of controls. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowControls
		{
			get
			{
				var vsp = ViewState["ShowControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the hex controls. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowHexControls
		{
			get
			{
				var vsp = ViewState["ShowHexControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowHexControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the hex summary. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowHexSummary
		{
			get
			{
				var vsp = ViewState["ShowHexSummary"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowHexControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the HSV controls. Default: false.")]
		[DefaultValue(false)]
		[Localizable(false)]
		public bool? ShowHSVControls
		{
			get
			{
				var vsp = ViewState["ShowHSVControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowHSVControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the RGB controls. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowRGBControls
		{
			get
			{
				var vsp = ViewState["ShowRGBControls"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowRGBControls"] = value;
			}
		}

		[Bindable(true)]
		[Category("Color Picker")]
		[Description("Hide/show the websafe-color swatch. Default: true.")]
		[DefaultValue(true)]
		[Localizable(false)]
		public bool? ShowWebSafe
		{
			get
			{
				var vsp = ViewState["ShowWebSafe"];
				if (vsp == null)
					return null;
				return (bool)vsp;
			}
			set
			{
				ViewState["ShowWebSafe"] = value;
			}
		}

		#endregion

		[DefaultValue(true)]
		public bool LoadYui
		{
			get
			{
				var vsp = ViewState["LoadYui"];
				if (vsp == null)
					return true;
				return (bool)vsp;
			}
			set
			{
				ViewState["LoadYui"] = value;
			}
		}

		#region Color Picker events

		[Category("Events")]
		[Description("The Color picker server-side ColorChanged handler")]
		[DefaultValue("")]
		[Localizable(false)]
		public event EventHandler ColorChanged;

		[Bindable(true)]
		[Category("Events")]
		[Description("The Color picker client-side ColorChanged handler code")]
		[DefaultValue("")]
		[Localizable(false)]
		public string OnClientColorChanged { get; set; }

		#endregion

		#region ASP.NET stuff
		/// <summary>
		/// Registers YUI and the initializer. On postbacks, loads the update value (which, in turn, will fire events)
		/// </summary>
		/// <param name="e"></param>
		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e); 
			
			NextControlInitializerFunctionId = ContainerClientID + "_loadyui";
			NextControlUpdateFunctionId = ContainerClientID + "_updateyui";

			if (ModalBehavior && ColorPickerMode == ColorPickerMode.Inline)
				throw new Exception("ModalBehavior cannot be set on ColorPickerMode Fixed");

			if (LoadYui)
			{
				Page.ClientScript.RegisterClientScriptInclude(GetType(), "YuiLoader", "http://yui.yahooapis.com/2.7.0/build/yuiloader/yuiloader-min.js");
			}
			if (!Page.IsPostBack)
			{

			}
			else if (Page.IsPostBack && !Page.IsCallback)
			{
				var val = Page.Request[HiddenFieldClientID];
				if (val.Length == 6)
					Color = "#" + val;			}
		}

		/// <summary>
		/// Initializes the script that will create the color picker on the next postback
		/// </summary>
		private string GetNextControlUpdateFunction()
		{
			var sb = new StringBuilder();
			sb.AppendLine("var picker = c.context;");
			//Not implemented
			return sb.ToString();
		}

		/// <summary>
		/// Returns the YUI loader code that will load the color picker and its dependencies, and then call this color picker instance's initializer
		/// </summary>
		/// <returns></returns>
		private string GetYuiLoaderCode()
		{
			string modules = "'colorpicker'";
			if (EnableAnimation)
				modules += ",'animation'";
			if (ColorPickerMode == ColorPickerMode.FloatingDialog)
				modules += ",'container','event'";

			return @"
(function() { 
    var loader = new YAHOO.util.YUILoader({ 
        base: '', 
        require: [" + modules + @"], 
        loadOptional: false, 
        combine: true, 
        filter: 'MIN', 
        allowRollup: true, 
        onSuccess: function() { " + NextControlInitializerFunctionId + @"();}
    }); 
loader.insert(); 
})();
";
		}

		/// <summary>
		/// The name of the function that will initialize this color picker instance
		/// </summary>
		string NextControlInitializerFunctionId;

		/// <summary>
		/// The name of the function that will update this color picker instance after the callback
		/// </summary>
		string NextControlUpdateFunctionId;

		/// <summary>
		/// Callback handler
		/// </summary>
		/// <param name="eventArg"></param>
		public void RaiseCallbackEvent(string eventArg)
		{
			Color = "#" + eventArg;
		}

		/// <summary>
		/// Callback handler
		/// </summary>
		/// <returns></returns>
		public string GetCallbackResult()
		{
			return "";
		}

		protected override HtmlTextWriterTag TagKey
		{
			get
			{
				return HtmlTextWriterTag.Div;
			}
		}

		/// <summary>
		/// Writes the color picker instance's initialization script
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			if (UseCallback)
				Page.ClientScript.RegisterStartupScript(GetType(), NextControlUpdateFunctionId, "function " + NextControlUpdateFunctionId + "(c) {" + GetNextControlUpdateFunction() + "}", true);

			Page.ClientScript.RegisterClientScriptBlock(GetType(), "YuiColorPickerJs", Code, true);

			int[] colors = ParseColor(Color);
			var script = new StringBuilder();
			script.Append("ASPYUI.CreateColorPicker(");
			script.AppendFormat("'{0}',", HiddenFieldClientID);
			script.AppendFormat("'{0}',", ContainerClientID);
			script.AppendFormat("function(){{{0}}},", (OnClientColorChanged ?? ""));
			script.AppendFormat("\"{0}\",", UseCallback ? Page.ClientScript.GetCallbackEventReference(this, "hex", NextControlUpdateFunctionId, "picker") : "");
			script.AppendFormat("'{0}',", ColorPickerMode.ToString());
			script.AppendFormat("'{0}',", DialogContainerClientID);
			script.AppendFormat("{0},", GetJsBool(ModalBehavior));
			script.AppendFormat("{0},", GetJsBool(IsHidden));
			script.AppendFormat("{0},", GetJsBool(ShowControls, true));
			script.AppendFormat("{0},", GetJsBool(ShowHexControls, true));
			script.AppendFormat("{0},", GetJsBool(ShowHexSummary, true));
			script.AppendFormat("{0},", GetJsBool(ShowHSVControls, false));
			script.AppendFormat("{0},", GetJsBool(ShowRGBControls, true));
			script.AppendFormat("{0},", GetJsBool(ShowWebSafe, true));
			script.AppendFormat("[{0},{1},{2}],", colors[0], colors[1], colors[2]);
			script.AppendFormat("'{0}');", UpdateMode.ToString());

			if (LoadYui)
			{
				script.Insert(0, "ASPYUI.Callbacks.push(function(){");
				script.Append("});");
				script.Append("ASPYUI.Modules.colorpicker = true;");
				if (EnableAnimation)
					script.Append("ASPYUI.Modules.animation = true;");
				if (ColorPickerMode == ColorPickerMode.FloatingDialog)
				{
					script.Append("ASPYUI.Modules.container = true;");
					script.Append("ASPYUI.Modules.event = true;");
				}
			}
			else
			{
				script.Insert(0, "YAHOO.util.Event.onDOMReady(function(){");
				script.Append("});");
			}

			Page.ClientScript.RegisterStartupScript(GetType(), NextControlInitializerFunctionId, script.ToString(), true);

			if (LoadYui)
				Page.ClientScript.RegisterStartupScript(GetType(), "LoadYUI", "ASPYUI.LoadYuiAsync();", true);

			Page.ClientScript.RegisterHiddenField(HiddenFieldClientID, Color);
		}



		/// <summary>
		/// Returns a javascript statement that hides the color picker
		/// </summary>
		/// <returns></returns>
		public string GetClientHidePickerScript()
		{
			switch (ColorPickerMode)
			{
				case ColorPickerMode.Inline:
					return "document.getElementById('" + ContainerClientID + "').style.display='none';";
				case ColorPickerMode.FloatingDialog:
					return "document.getElementById('" + DialogContainerClientID + "').dialog.cancel();";
				default:
					return "";
			}
		}

		/// <summary>
		/// Returns a javascript statement that shows the color picker
		/// </summary>
		/// <returns></returns>
		public string GetClientShowPickerScript()
		{
			switch (ColorPickerMode)
			{
				case ColorPickerMode.Inline:
					return "document.getElementById('" + ContainerClientID + "').style.display='';";
				case ColorPickerMode.FloatingDialog:
					return "document.getElementById('" + DialogContainerClientID + "').dialog.show();";
				default:
					return "";
			}
		}

		/// <summary>
		/// Return a javascript expression that evaluates to "#rrggbb"
		/// </summary>
		/// <returns></returns>
		public string GetClientGetPickerColorScript()
		{
			return "document.getElementById('" + HiddenFieldClientID + "').value";
		}
		#endregion

		#region Utils
		/// <summary>
		/// Used to parse an RGB color from value into the Color Picker widget
		/// </summary>
		/// <param name="RGB"></param>
		/// <returns></returns>
		private int[] ParseColor(string RGB)
		{
			int[] ret = new int[3];
			if (String.IsNullOrEmpty(RGB))
				RGB = "ffffff";
			if (RGB.StartsWith("#") || RGB.Length == 6)
			{
				if (RGB.StartsWith("#"))
					RGB = RGB.Substring(1);
				if (RGB.Length == 3)
				{
					var r = RGB.Substring(0, 1);
					ret[0] = int.Parse(r, System.Globalization.NumberStyles.HexNumber);
					var g = RGB.Substring(1, 1);
					ret[1] = int.Parse(g, System.Globalization.NumberStyles.HexNumber);
					var b = RGB.Substring(2, 1);
					ret[2] = int.Parse(b, System.Globalization.NumberStyles.HexNumber);
				}
				else if (RGB.Length == 6)
				{
					var r = RGB.Substring(0, 2);
					ret[0] = int.Parse(r, System.Globalization.NumberStyles.HexNumber);
					var g = RGB.Substring(2, 2);
					ret[1] = int.Parse(g, System.Globalization.NumberStyles.HexNumber);
					var b = RGB.Substring(4, 2);
					ret[2] = int.Parse(b, System.Globalization.NumberStyles.HexNumber);
				}
				else throw new Exception("Invalid hex RGB value");
			}
			else if (RGB.Split(',').Length == 3)
			{
				var parts = RGB.Split(',');
				var r = parts[0];
				ret[0] = Int32.Parse(r);
				var g = parts[0];
				ret[1] = Int32.Parse(r);
				var b = parts[0];
				ret[2] = Int32.Parse(r);
			}
			else throw new Exception("Invalid color value");

			if (ret[0] < 0 || ret[0] > 255)
				throw new Exception("Invalid Red component value");
			if (ret[1] < 0 || ret[1] > 255)
				throw new Exception("Invalid Green component value");
			if (ret[2] < 0 || ret[2] > 255)
				throw new Exception("Invalid Blue component value");
			return ret;
		}

		/// <summary>
		/// Returns either true or false, with a default value.
		/// </summary>
		/// <param name="value"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private string GetJsBool(bool? value, bool defaultValue)
		{
			return GetJsBool(value.GetValueOrDefault(defaultValue));
		}

		private string GetJsBool(bool v)
		{
			return v ? "true" : "false";
		}

		#endregion

		protected override void RenderContents(HtmlTextWriter writer)
		{
			writer.WriteBeginTag("div");
			writer.WriteAttribute("id", DialogContainerClientID);

			if (ColorPickerMode == ColorPickerMode.FloatingDialog)
			{
				writer.WriteAttribute("class", "yui-picker-panel");
				writer.WriteAttribute("visibility", "hidden");
				writer.Write(HtmlTextWriter.TagRightChar);

				writer.WriteBeginTag("div");
				writer.WriteAttribute("class", "hd");
				writer.Write(HtmlTextWriter.TagRightChar);
				writer.WriteEncodedText("Please choose a color:");
				writer.WriteEndTag("div");

				writer.WriteBeginTag("div");
				writer.WriteAttribute("class", "bd");
				writer.Write(HtmlTextWriter.TagRightChar);
			}
			else
				writer.Write(HtmlTextWriter.TagRightChar);


			writer.WriteBeginTag("div");
			writer.WriteAttribute("id", ContainerClientID);
			writer.WriteAttribute("style", "position:relative;height:220px");//width:420px;
			writer.Write(HtmlTextWriter.SelfClosingTagEnd);

			if (ColorPickerMode == ColorPickerMode.FloatingDialog)
			{
				writer.WriteEndTag("div"); //close the body div

				writer.WriteBeginTag("div");
				writer.WriteAttribute("class", "ft");
				writer.Write(HtmlTextWriter.TagRightChar);
				//writer.WriteEncodedText("Footer text");        
				writer.WriteEndTag("div");
			}

			writer.WriteEndTag("div");
		}


		const string Code = @"
if (typeof(ASPYUI)=='undefined') 
  ASPYUI = {};
if (typeof(ASPYUI.Callbacks)=='undefined')   
  ASPYUI.Callbacks=[];
if (typeof(ASPYUI.Modules)=='undefined')   
  ASPYUI.Modules={};
if (typeof(ASPYUI.LoadYuiAsync)=='undefined') 
  ASPYUI.LoadYuiAsync = function()
  {
    window.setTimeout(ASPYUI.LoadYui,0);
  }
if (typeof(ASPYUI.LoadYui)=='undefined') 
  ASPYUI.LoadYui = function()
  {
    var modules = [];
    for (var i in ASPYUI.Modules)
      modules.push(i);
    var loader = new YAHOO.util.YUILoader(
    { 
      base: '', 
      require: modules, 
      loadOptional: false, 
      combine: true, 
      filter: 'MIN', 
      allowRollup: true, 
      onSuccess: function() 
      { 
        for (var j=0;j<ASPYUI.Callbacks.length;j++)
        try
        {
          ASPYUI.Callbacks[j]();
        }
        catch(e1)
        {
        }
      }
    }); 
    loader.insert();
  }

ASPYUI.CreateColorPicker = function(  HiddenFieldClientID, 
      ContainerClientID, 
      OnClientValueChanged, 
      CallbackRef, 
      ColorPickerMode, 
      DialogContainerClientID, 
      ModalBehavior, 
      IsHidden,
      ShowControls,
      ShowHexControls,
      ShowHexSummary,
      ShowHSVControls,
      ShowRGBControls,
      ShowWebSafe,
      RGB, //[int,int,int]
      UpdateMode
      )
{
  var picker, dialog, container, hiddenField, valueChangeHandler, hex, revertValueHandler;
  hiddenField = document.getElementById(HiddenFieldClientID);
  container = new YAHOO.util.Element(ContainerClientID);
  new YAHOO.util.Element(document.body).addClass('yui-skin-sam');
  //Create value changed handler
  function valueChangeHandler() 
  {
    hex = picker.get('hex');
    if (HiddenFieldClientID != null)
      hiddenField.value = hex;
    if (OnClientValueChanged != null)
    {
      try
      {
        OnClientValueChanged();
      } 
      catch(e1)
      {
      }
    }
    if (CallbackRef!=null)
    {
      try 
      { 
        eval(CallbackRef+';');
      } 
      catch(e2)
      {
      }
    }  
    }
  
  //Create revert handler
  revertValueHandler = function() {picker.set('hex',dialog._initialPickerValue);}

  function setupControl()
  {
    picker = new YAHOO.widget.ColorPicker(container, 
    {
      showcontrols: ShowControls
      ,showhexcontrols: ShowHexControls
      ,showhexsummary: ShowHexSummary
      ,showhsvcontrols: ShowHSVControls
      ,showrgbcontrols: ShowRGBControls
      ,showwebsafe: ShowWebSafe 
      ,images: 
      {
        PICKER_THUMB: 'http://yui.yahooapis.com/2.7.0/build/colorpicker/assets/picker_thumb.png', 
        HUE_THUMB: 'http://yui.yahooapis.com/2.7.0/build/colorpicker/assets/hue_thumb.png'     
      }
      ,container: ((ColorPickerMode == ColorPickerMode.FloatingDialog) ? dialog : undefined)
    });
    
    container.get('element')._picker = picker;
    if (RGB)
    {
      picker.skipAnim = true;
      picker.setValue(RGB, true);
      picker.skipAnim = false;
    }
    
    if (ColorPickerMode == 'FloatingDialog')
    {
      if (UpdateMode=='Continuous')
      {
        picker.on('rgbChange', valueChangeHandler);
      }
      
    } 
    else if (ColorPickerMode == 'Inline')
    {
      if (UpdateMode == 'Continuous')
        picker.on('rgbChange', valueChangeHandler);        
    }
    hiddenField.value = picker.get('hex');    
    
  }

  if (ColorPickerMode == 'FloatingDialog')
    {
        
        dialog = new YAHOO.widget.Dialog(DialogContainerClientID, 
    { 
        width : '500px',
        close: false,
        fixedcenter : true,
        visible : false,
        modal : ModalBehavior,
        zindex: 4,
        constraintoviewport : true,
        buttons : [ 
          { text:'OK',handler: function(){dialog.cancel(); valueChangeHandler(); }, isDefault:true },
          { text:'Cancel', handler: function(){revertValueHandler(); dialog.cancel(); } } ]
        });
        dialog.showEvent.subscribe(function() { dialog._initialPickerValue=picker.get('hex'); });
        dialog.renderEvent.subscribe(function() {setupControl();});
    dialog.render();
    document.getElementById(DialogContainerClientID).dialog = dialog;    
    if (!IsHidden)
        {
          dialog.show();
        }
      }
      else
      {
        container.addClass('yui-skin-sam');
        if (IsHidden)
          container.setStyle('display','none');
    setupControl();
      }
};";
	}
}